<?php
/************************
 *  Codenova.pl Project
 *  Created by: Kamil Mrzygłód
 *
 *  (c) 2013 by Codenova.pl
 *  All Right Reserved
 ***********************/

namespace Gotcha\App\Framework;

// Gotcha framework

use Gotcha\App\Extensions\ErrorsExtension;
use Gotcha\App\Extensions\FileSystemExtension;
use Gotcha\App\Extensions\HttpExtension;
use Gotcha\App\Extensions\ViewsExtension;
use Gotcha\App\GotchaBootstrap;
use Gotcha\App\Types\Arrays;
use Gotcha\App\Types\Int;
use Gotcha\App\Types\String;

class Controller extends GotchaBootstrap {
    // View file name handler
    protected $viewFileNameHandler = '';

    // Accepted methods
    private $acceptedMethods = array('View', 'RedirectToAction', 'UrlContent');

    // Methods overloader
    public function __call($method, $params) {
        if (in_array($method, $this->acceptedMethods)) {
            switch ($method) {
                case 'View' :
                    switch(count($params)) {
                        case 0:
                            return $this->ViewDefault();
                            break;
                        case 1:
                            switch(gettype($params[0])) {
                                case 'array':
                                    return $this->View1Array(new Arrays($params[0]));
                                    break;
                                default:
                                    $amountOfParams = new Int(count($params));
                                    ErrorsExtension::throwErrorAndSaveLog(new String('OVERLOAD_ERROR_INVALID_PARAMETER_TYPE'), new Arrays(array(new String('GotchaFramework::'.$method.'()'), $amountOfParams->int, new String(gettype($params[0])))));
                            }
                            break;
                        case 2:
                            switch(gettype($params[0])) {
                                case 'array':
                                    switch(gettype($params[1])) {
                                        case 'string':
                                            return $this->View1Array2String(new Arrays($params[0]), new String($params[1]));
                                            break;
                                        default:
                                            $amountOfParams = new Int(count($params));
                                            ErrorsExtension::throwErrorAndSaveLog(new String('OVERLOAD_ERROR_INVALID_PARAMETER_TYPE'), new Arrays(array(new String('GotchaFramework::'.$method.'()'), $amountOfParams->int, new String(gettype($params[0])))));
                                    }
                                    break;
                                default:
                                    $amountOfParams = new Int(count($params));
                                    ErrorsExtension::throwErrorAndSaveLog(new String('OVERLOAD_ERROR_INVALID_PARAMETER_TYPE'), new Arrays(array(new String('GotchaFramework::'.$method.'()'), $amountOfParams->int, new String(gettype($params[0])))));
                            }
                            break;
                        default:
                            $amountOfParams = new Int(count($params));
                            ErrorsExtension::throwErrorAndSaveLog(new String('OVERLOAD_ERROR_INVALID_PARAMETER_AMOUNT'), new Arrays(array(new String('GotchaFramework::'.$method.'()'), $amountOfParams->int)));
                    }
                    break;
                case 'UrlContent':
                    switch(count($params)) {
                        case 1:
                            switch(gettype($params[0])) {
                                case 'string':
                                    return $this->UrlContentString(new String($params[0]));
                                    break;
                                default:
                                    $amountOfParams = new Int(count($params));
                                    ErrorsExtension::throwErrorAndSaveLog(new String('OVERLOAD_ERROR_INVALID_PARAMETER_TYPE'), new Arrays(array(new String('GotchaFramework::'.$method.'()'), $amountOfParams->int, new String(gettype($params[0])))));
                            }
                            break;
                        default:
                            $amountOfParams = new Int(count($params));
                            ErrorsExtension::throwErrorAndSaveLog(new String('OVERLOAD_ERROR_INVALID_PARAMETER_AMOUNT'), new Arrays(array(new String('GotchaFramework::'.$method.'()'), $amountOfParams->int)));
                    }
                    break;
                case 'RedirectToAction':
                    switch(count($params)) {
                        case 1:
                            switch(gettype($params[0])) {
                                case 'string':
                                    $this->RedirectToAction1String(new String($params[0]));
                                    break;
                                default:
                                    $amountOfParams = new Int(count($params));
                                    ErrorsExtension::throwErrorAndSaveLog(new String('OVERLOAD_ERROR_INVALID_PARAMETER_TYPE'), new Arrays(array(new String('GotchaFramework::'.$method.'()'), $amountOfParams->int, new String(gettype($params[0])))));
                            }
                            break;
                        case 2:
                            switch(gettype($params[0])) {
                                case 'string':
                                    switch(gettype($params[1])) {
                                        case 'string':
                                            $this->RedirectToAction1String2String(new String($params[0]), new String($params[1]));
                                            break;
                                        default:
                                            $amountOfParams = new Int(count($params));
                                            ErrorsExtension::throwErrorAndSaveLog(new String('OVERLOAD_ERROR_INVALID_PARAMETER_TYPE'), new Arrays(array(new String('GotchaFramework::'.$method.'()'), $amountOfParams->int, new String(gettype($params[0])))));
                                    }
                                    break;
                                default:
                                    $amountOfParams = new Int(count($params));
                                    ErrorsExtension::throwErrorAndSaveLog(new String('OVERLOAD_ERROR_INVALID_PARAMETER_TYPE'), new Arrays(array(new String('GotchaFramework::'.$method.'()'), $amountOfParams->int, new String(gettype($params[0])))));
                            }
                            break;
                        default:
                            $amountOfParams = new Int(count($params));
                            ErrorsExtension::throwErrorAndSaveLog(new String('OVERLOAD_ERROR_INVALID_PARAMETER_AMOUNT'), new Arrays(array(new String('GotchaFramework::'.$method.'()'), $amountOfParams->int)));
                    }
                    break;
            }
        } else {
            ErrorsExtension::throwErrorAndSaveLog(new String('OVERLOAD_ERROR_NO_METHOD'), new Arrays(array(new String('GotchaFramework::'.$method))));
        }
    }

    // View()
    private function ViewDefault() {
        // If there is no view given, method
        // searches for a view stored in
        // Modules/[MODULE_NAME]/View/[CONTROLLER_NAME]/
        $this->getViewNameToBeCalled();

        // Now when we have handler for a view file
        // we can go further and enable whole themed
        // stuff
        $viewLayout = new ViewsExtension();
        $viewLayout->Display('Document', $this->viewFileNameHandler);
    }

    private function View1Array(Arrays $variables) {
        // Function renders a view with given variables
        $this->getViewNameToBeCalled();

        $viewLayout = new ViewsExtension();
        $viewLayout->Display($variables->array, 'Document', $this->viewFileNameHandler);
    }

    private function View1Array2String(Arrays $variables, String $viewName) {
        // Function render given view and passes given variables.
        // Unlike previous overloads it doesn't include
        // searching for a view in module directory
        $viewLayout = new ViewsExtension();
        $viewLayout->Display($variables->array, $viewName->string);
    }

    // RedirectToAction()
    private function RedirectToAction1String(String $action) {
        // We use debug_backtrace() to get class name
        $backtrace = debug_backtrace();

        // Load current class name
        $backtraceClass = new String($backtrace[4]['class']);

        // Since we load full class name(equivalent to
        // its namespace) we have to extract what's
        // the most interesting - module name
        $explodedClassName = explode('\\', $backtraceClass);
        $className = new String($explodedClassName[1]);

        // With current class name we can find in
        // routing repository path to redirect to
        $routes = FileSystemExtension::returnRoutesValues();
        foreach($routes as $routeUrl => $route) {
            if($route['module'] == $className) {
                if($route['action'] == $action) {
                    // HTTP protocol defines, that we need
                    // full absolute URL to create a proper
                    // header
                    $absoluteUrl = new HttpExtension();

                    // Redirect
                    header('Location: '.$absoluteUrl->resolveAbsoluteUrl().$routeUrl.'');
                    exit;
                }
            }
        }

        // If we get that far, throw an error
        ErrorsExtension::throwErrorAndSaveLog(new String('CANNOT_REDIRECT_TO_ACTION'), new Arrays(array($action)));
    }

    private function RedirectToAction1String2String(String $action, String $controller) {
        // Function is similar to RedirectToAction1String()
        // but calls action from given controller

        // We use debug_backtrace() to get class name
        $backtrace = debug_backtrace();

        // Load current class name
        $backtraceClass = new String($backtrace[4]['class']);

        // Since we load full class name(equivalent to
        // its namespace) we have to extract what's
        // the most interesting - module name
        $explodedClassName = explode('\\', $backtraceClass);
        $className = new String($explodedClassName[1]);

        // With current class name we can find in
        // routing repository path to redirect to
        $routes = FileSystemExtension::returnRoutesValues();
        foreach($routes as $routeUrl => $route) {
            if($route['module'] == $className) {
                if($route['controller'] == $controller) {
                    if($route['action'] == $action) {
                        // HTTP protocol defines, that we need
                        // full absolute URL to create a proper
                        // header
                        $absoluteUrl = new HttpExtension();

                        // Redirect
                        header('Location: '.$absoluteUrl->resolveAbsoluteUrl().$routeUrl.'');
                        exit;
                    }
                }
            }
        }

        // If we get that far, throw an error
        ErrorsExtension::throwErrorAndSaveLog(new String('CANNOT_REDIRECT_TO_ACTION'), new Arrays(array($action)));
    }

    // UrlContent()
    private function UrlContentString($url) {
        return GOTCHA_BASE_PATH.$url;
    }

    // Internal class methods
    private function getViewNameToBeCalled() {
        // We use debug_backtrace() to get method
        // and controller names
        $backtrace = debug_backtrace();

        // Since debug_backtrace() returns an array,
        // we are interested in second key
        $backtraceFunction = $backtrace[4]['function'];
        $backtraceClass = $backtrace[4]['class'];

        // Class is returned with whole namespace
        // so there is a need to clean it up
        $backtraceClassExploded = explode('\\', $backtraceClass);
        $backtraceClass = str_replace('Controller', '', $backtraceClassExploded[count($backtraceClassExploded) - 1]);

        // Now when we have both class - which represents
        // subdirectory in View directory - and function name
        // - which represent view name - we can load a view
        if (FileSystemExtension::CheckDirExistence(new String('Modules/'.$backtraceClassExploded[1].'/Views/'.$backtraceClass)) === true) {
            // Corresponding directory exists
            if (FileSystemExtension::CheckFileExistence(new String($backtraceFunction.'.phtml'), new String('Modules/'.$backtraceClassExploded[1].'/Views/'.$backtraceClass.'/'))) {
                // View file exists
                $this->viewFileNameHandler = 'Modules\\'.$backtraceClassExploded[1].'\Views\\'.$backtraceClass.'\\'.$backtraceFunction.'.phtml';
            } else {
                ErrorsExtension::throwErrorAndSaveLog(new String('REQUESTED_VIEW_FILE_DOESNT_EXIST'), new Arrays(array(new String('Modules/'.$backtraceClassExploded[1].'/Views/'.$backtraceClass.'/'.$backtraceFunction.'.phtml'))));
            }
        } else {
            ErrorsExtension::throwErrorAndSaveLog(new String('REQUESTED_VIEW_DIR_DOESNT_EXIST'), new Arrays(array(new String('Modules/'.$backtraceClassExploded[1].'/Views/'.$backtraceClass))));
        }
    }
}